import 'dart:math' as math;

import 'package:flutter/material.dart';

class StretchableGridView extends StatefulWidget {
  final double minHeight;
  final Duration stretchDuration;
  final EdgeInsetsGeometry padding;
  final SliverGridDelegate gridDelegate;
  final IndexedWidgetBuilder itemBuilder;
  final int itemCount;

  const StretchableGridView({
    this.minHeight = 200,
    this.stretchDuration = kThemeAnimationDuration,
    this.padding,
    this.gridDelegate = const SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 8,
      childAspectRatio: 1,
    ),
    @required this.itemBuilder,
    @required this.itemCount,
  })  : assert(minHeight > 0),
        assert(stretchDuration != null),
        assert(gridDelegate != null),
        assert(itemBuilder != null),
        assert(itemCount != null);

  @override
  State<StatefulWidget> createState() {
    return StretchableGridViewState();
  }
}

class StretchableGridViewState extends State<StretchableGridView> {
  final _scrollController = ScrollController();
  Tween<double> _animation;

  @override
  void initState() {
    super.initState();

    _animation = Tween<double>(end: widget.minHeight);
    _scrollController.addListener(_onScroll);
  }

  @override
  void dispose() {
    _scrollController.removeListener(_onScroll);

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return TweenAnimationBuilder(
      tween: _animation,
      duration: widget.stretchDuration,
      builder: (context, value, child) {
        return ConstrainedBox(
          constraints: BoxConstraints(
            minWidth: MediaQuery.of(context).size.width,
            maxHeight: value,
          ),
          child: child,
        );
      },
      child: GridView.builder(
        controller: _scrollController,
        physics: const BouncingScrollPhysics(),
        shrinkWrap: true,
        padding: widget.padding,
        gridDelegate: widget.gridDelegate,
        itemBuilder: widget.itemBuilder,
        itemCount: widget.itemCount,
      ),
    );
  }

  void _onScroll() {
    double maxHeight;
    if (_scrollController.hasClients && _scrollController.offset > widget.minHeight) {
      maxHeight = math.max(widget.minHeight, MediaQuery.of(context).size.height / 2);
    } else {
      maxHeight = widget.minHeight;
    }

    if (_animation.end != maxHeight) {
      setState(() {
        _animation = Tween<double>(end: maxHeight);
      });
    }
  }
}
